# Código do modelo matemático - Desafio Bootcamp - Enacom
# Autoria: Cassiano Tavares
# Verificação se todas as bibliotecas necessárias estão instaladas. Caso contrário, ~\
# esta célula irá realizar a instalação das bibliotecas ainda nao instaladas
import pip
def import_or_install(pac):
try:
__import__(pac)
except ImportError:
pip.main(['install', pac])
bibliotecas = ["tkinter", "pandas", "numpy", "math", "datetime","io","pyomo","re","collections","plotly.express","plotly.graph_objects"]
for b in bibliotecas:
import_or_install(b)
print("Bibliotecas instaladas")
Bibliotecas instaladas
# Carrega as bibliotecas do Python
from tkinter import E
import pandas as pd
import numpy as np
import math
#from google.colab import files
import datetime
import io
import pyomo.environ as pyo
from pyomo.environ import *
from pyomo.opt import SolverFactory
import re
import sys
from collections import defaultdict
import plotly.express as px
import plotly.graph_objects as go
# Define o arquivo com dados de entrada
nome_input = 'dados_desafio_2023.xlsx'
# Lê os dados de entrada como data frames
# Carteira de Investimentos (IGNORA LINHAS COM CÉLULAS EM BRANCO)
df_carteira_investimentos = pd.read_excel(nome_input, sheet_name='MASTER_40').dropna()
print(df_carteira_investimentos)
Opção Descrição \
0 1 MASTER 5500 D - PADRÃO - BRA
1 2 MASTER 5500 D C/ ESTEIRA PRECISA - PADRÃO - BRA
2 3 MASTER 5500 D C/ ESTEIRA PRECISA C/ PNEU 11L15...
3 4 MASTER 5500 D C/ DIRECIONADOR - PADRÃO - BRA
4 5 MASTER 7500 D - PADRÃO - BRA
5 6 MASTER 7500 D C/ ESTEIRA PRECISA - PADRÃO - BRA
6 7 MASTER 7500 D C/ PNEU 11L15 - PADRÃO - BRA
7 8 MASTER 7500 D C/ ESTEIRA PRECISA C/ PNEU 11L15...
8 9 MASTER 2500 D - PADRÃO - BRA
9 10 MASTER 2500 D C/ ESTEIRA PRECISA - PADRÃO - BRA
10 11 MASTER 2500 D CAFEEIRA C/ ESTEIRA PRECISA - PA...
11 12 MASTER 2500 D CAFEEIRA C/ ESTEIRA PRECISA C/ D...
12 13 MASTER 5500 D - VERMELHO - BRA
13 14 MASTER 5500 D - VERDE - BRA
14 15 MASTER 5500 D - AMARELO - BRA
15 16 MASTER 5500 D - AZUL - BRA
16 17 MASTER 5500 D C/ ESTEIRA PRECISA - VERMELHO - BRA
17 18 MASTER 5500 D C/ ESTEIRA PRECISA - VERDE - BRA
18 19 MASTER 5500 D C/ ESTEIRA PRECISA - AMARELO - BRA
19 20 MASTER 5500 D C/ ESTEIRA PRECISA - AZUL - BRA
20 21 MASTER 5500 D C/ ESTEIRA PRECISA C/ PNEU 11L15...
21 22 MASTER 5500 D C/ ESTEIRA PRECISA C/ PNEU 11L15...
22 23 MASTER 5500 D C/ ESTEIRA PRECISA C/ PNEU 11L15...
23 24 MASTER 5500 D C/ ESTEIRA PRECISA C/ PNEU 11L15...
24 25 MASTER 5500 D C/ DIRECIONADOR - VERMELHO - BRA
25 26 MASTER 5500 D C/ DIRECIONADOR - VERDE - BRA
26 27 MASTER 5500 D C/ DIRECIONADOR - AMARELO - BRA
27 28 MASTER 5500 D C/ DIRECIONADOR - AZUL - BRA
28 29 MASTER 7500 D - VERMELHO - BRA
29 30 MASTER 7500 D - VERDE - BRA
30 31 MASTER 7500 D - AMARELO - BRA
31 32 MASTER 7500 D - AZUL - BRA
32 33 MASTER 7500 D C/ ESTEIRA PRECISA - VERMELHO - BRA
33 34 MASTER 7500 D C/ ESTEIRA PRECISA - VERDE - BRA
34 35 MASTER 7500 D C/ ESTEIRA PRECISA - AMARELO - BRA
35 36 MASTER 7500 D C/ ESTEIRA PRECISA - AZUL - BRA
36 37 MASTER 7500 D C/ PNEU 11L15 - VERMELHO - BRA
37 38 MASTER 7500 D C/ PNEU 11L15 - VERDE - BRA
38 39 MASTER 7500 D C/ PNEU 11L15 - AMARELO - BRA
39 40 MASTER 7500 D C/ PNEU 11L15 - AZUL - BRA
Custo do investimento (R$) Retorno esperado (R$)
0 9706 22804
1 16791 15932
2 12953 20329
3 10264 29465
4 14240 21801
5 13063 32694
6 14314 32016
7 15599 17799
8 15073 25006
9 16920 32458
10 16617 21419
11 9649 22287
12 13997 24908
13 14180 27160
14 10317 16648
15 13106 31571
16 14531 26113
17 13828 18250
18 14556 16982
19 9084 22599
20 11382 24161
21 11728 18533
22 11144 33029
23 16558 16528
24 16014 24586
25 9500 15061
26 11706 33897
27 12893 22713
28 12210 15630
29 14636 32881
30 13256 34890
31 16708 28002
32 14881 18796
33 12066 17861
34 14448 33380
35 15126 29959
36 16814 25048
37 15835 19950
38 14239 34103
39 14647 28654
# Criação dos conjuntos
# Criação do conjunto de opções invenstimentos
dict_I = dict(enumerate(df_carteira_investimentos["Opção"]))
print("Este é o conjunto de opções investimentos")
print(dict_I)
Este é o conjunto de opções investimentos
{0: 1, 1: 2, 2: 3, 3: 4, 4: 5, 5: 6, 6: 7, 7: 8, 8: 9, 9: 10, 10: 11, 11: 12, 12: 13, 13: 14, 14: 15, 15: 16, 16: 17, 17: 18, 18: 19, 19: 20, 20: 21, 21: 22, 22: 23, 23: 24, 24: 25, 25: 26, 26: 27, 27: 28, 28: 29, 29: 30, 30: 31, 31: 32, 32: 33, 33: 34, 34: 35, 35: 36, 36: 37, 37: 38, 38: 39, 39: 40}
#Criação do parâmetro c_{i} - Custo de investimento do projeto
# Inicializa a matriz com zeros
c_i = [0 for e in dict_I.keys()]
# Preenche as posições de acordo com df_carteira_investimentos
for linha in df_carteira_investimentos.iterrows():
# Extrai o conteúdo da linha atual
dados_linha = list(linha[1])
#print(dados_linha)
# Define o índice do projeto de investimento
try:
# Procura o índice na lista do projeto de investimento
i = list(dict_I.keys())[list(dict_I.values()).index(dados_linha[0])]
c_i[i]= dados_linha[2]
except:
# Projeto de investimento não encontrado
i = -1
print(c_i)
[9706, 16791, 12953, 10264, 14240, 13063, 14314, 15599, 15073, 16920, 16617, 9649, 13997, 14180, 10317, 13106, 14531, 13828, 14556, 9084, 11382, 11728, 11144, 16558, 16014, 9500, 11706, 12893, 12210, 14636, 13256, 16708, 14881, 12066, 14448, 15126, 16814, 15835, 14239, 14647]
#Criação do parâmetro vpl_{i} - VPL do projeto
# Inicializa a matriz com zeros
vpl_i = [0 for e in dict_I.keys()]
# Preenche as posições de acordo com df_carteira_investimentos
for linha in df_carteira_investimentos.iterrows():
# Extrai o conteúdo da linha atual
dados_linha = list(linha[1])
#print(dados_linha)
# Define o índice do projeto de investimento
try:
# Procura o índice na lista do projeto de investimento
i = list(dict_I.keys())[list(dict_I.values()).index(dados_linha[0])]
vpl_i[i]= dados_linha[3]
except:
# Projeto de investimento não encontrado
i = -1
print(vpl_i)
[22804, 15932, 20329, 29465, 21801, 32694, 32016, 17799, 25006, 32458, 21419, 22287, 24908, 27160, 16648, 31571, 26113, 18250, 16982, 22599, 24161, 18533, 33029, 16528, 24586, 15061, 33897, 22713, 15630, 32881, 34890, 28002, 18796, 17861, 33380, 29959, 25048, 19950, 34103, 28654]
# Budget disponível para investimento
M = 300000
# Contra parte robusta
#Parâmetro zeta que define o nível de incerteza
zeta = 0.15
# Parâmetro gamma que definide o budget de incerteza
gamma = len(dict_I.keys())
# Criacao do modelo de otimização
model = pyo.ConcreteModel()
#Criação dos conjuntos do modelo de otimização
# Conjunto de opções invenstimentos
model.set_I = pyo.Set(initialize = dict_I.keys())
print("Este é o model.set_I")
print(dict_I.keys())
Este é o model.set_I dict_keys([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39])
#Criação da variável de decisão
# Variaáel de decisão: 1 se o projeto de investimento é selecionado
model.Y = pyo.Var(model.set_I, within=Binary)
Y = model.Y
# Criação das variáveis de incerteza
# Variável dual do domínio da restrição do budget de incerteza
model.Lambda = pyo.Var(within=NonNegativeReals)
Lambda = model.Lambda
# Variável dual do domínio da restrição do domínio de zeta
model.Mu = pyo.Var(model.set_I, within=NonNegativeReals)
Mu = model.Mu
# Função objetivo do modelo
model.exprobj = pyo.Expression()
#Primeiro termo da Função Objetivo: Cálculo do VPL de todos os projetos
obj1 = sum(vpl_i[i]*Y[i] for i in model.set_I)
#Segundo termo da Função Objetivo: Contraparte Robusta
obj2 = gamma*Lambda
# Função objetivo:
model.exprobj = obj1 - obj2
model.obj = pyo.Objective(expr= model.exprobj, sense = pyo.maximize)
#Restrições 02: Mochila
model.C2 = pyo.ConstraintList()
model.C2.add(expr=sum(Y[i]*c_i[i] for i in model.set_I) <= M)
<pyomo.core.base.constraint._GeneralConstraintData at 0xd35d1832e0>
#Restrições 03: Se o investimento 1 for selecionado, então o investimento 5 não deve ser
model.C3a = pyo.ConstraintList()
model.C3a.add(expr = Y[0] <= M*(1-Y[4]))
model.C3b= pyo.ConstraintList()
model.C3b.add(expr = Y[4] <= M*(Y[1]))
<pyomo.core.base.constraint._GeneralConstraintData at 0xd35d0eeca0>
#Restrições 04: Se o investimento 2 for selecionado, então o investimento 4 também deve ser.
model.C4 = pyo.ConstraintList()
model.C4.add(expr = Y[1] <= Y[3])
<pyomo.core.base.constraint._GeneralConstraintData at 0xd35d0eed00>
#Restrições 05: Contraparte robusta
model.C5 = pyo.ConstraintList()
for i in model.set_I:
model.C5.add(expr = Lambda + Mu[i] >= c_i[i] * Y[i])
#Restrições 06 e 07: Domínio das variáveis de incerteza
model.C6 = pyo.ConstraintList()
model.C7 = pyo.ConstraintList()
for i in model.set_I:
model.C6.add(expr=Lambda >= 0)
model.C7.add(expr=Mu[i] >= 0)
# Restrições 08: Domínio da variável de decisão
model.C8 = pyo.ConstraintList()
for i in model.set_I:
model.C8.add(expr=Y[i] >= 0)
# Escreve modelo em arquivo no formato LP
model.write('model.lp', io_options={'symbolic_solver_labels': True})
# Seleciona o solver
solver = pyo.SolverFactory('glpk')
solution = solver.solve(model, tee=True)
from pyomo.opt import SolverStatus, TerminationCondition
if (solution.solver.status == SolverStatus.ok) and (solution.solver.termination_condition == TerminationCondition.optimal):
print("Solution is feasible and optimal")
print("Objective function value = ", model.obj())
elif solution.solver.termination_condition == TerminationCondition.infeasible:
print ("Failed to find solution.")
else:
# something else is wrong
print(str(solution.solver))
GLPSOL--GLPK LP/MIP Solver 5.0 Parameter(s) specified in the command line: --write C:\Users\CASSIA~1\AppData\Local\Temp\tmpb6ikms_5.glpk.raw --wglp C:\Users\CASSIA~1\AppData\Local\Temp\tmper_umsvq.glpk.glp --cpxlp C:\Users\CASSIA~1\AppData\Local\Temp\tmpr14sq5j2.pyomo.lp Reading problem data from 'C:\Users\CASSIA~1\AppData\Local\Temp\tmpr14sq5j2.pyomo.lp'... C:\Users\CASSIA~1\AppData\Local\Temp\tmpr14sq5j2.pyomo.lp:913: warning: lower bound of variable 'x1' redefined C:\Users\CASSIA~1\AppData\Local\Temp\tmpr14sq5j2.pyomo.lp:913: warning: upper bound of variable 'x1' redefined 165 rows, 82 columns, 287 non-zeros 40 integer variables, all of which are binary 953 lines were read Writing problem data to 'C:\Users\CASSIA~1\AppData\Local\Temp\tmper_umsvq.glpk.glp'... 785 lines were written GLPK Integer Optimizer 5.0 165 rows, 82 columns, 287 non-zeros 40 integer variables, all of which are binary Preprocessing... 2 hidden packing inequaliti(es) were detected 44 rows, 81 columns, 166 non-zeros 40 integer variables, all of which are binary Scaling... A: min|aij| = 1.000e+00 max|aij| = 1.692e+04 ratio = 1.692e+04 GM: min|aij| = 8.613e-01 max|aij| = 1.161e+00 ratio = 1.348e+00 EQ: min|aij| = 7.615e-01 max|aij| = 1.000e+00 ratio = 1.313e+00 2N: min|aij| = 5.000e-01 max|aij| = 1.109e+00 ratio = 2.218e+00 Constructing initial basis... Size of triangular part is 44 Solving LP relaxation... GLPK Simplex Optimizer 5.0 44 rows, 81 columns, 166 non-zeros * 0: obj = -0.000000000e+00 inf = 0.000e+00 (40) * 71: obj = 6.579945809e+05 inf = 0.000e+00 (0) OPTIMAL LP SOLUTION FOUND Integer optimization begins... Long-step dual simplex will be used + 71: mip = not found yet <= +inf (1; 0) + 94: >>>>> 6.317210000e+05 <= 6.574957348e+05 4.1% (23; 0) + 184: >>>>> 6.446020000e+05 <= 6.571380483e+05 1.9% (69; 2) + 231: >>>>> 6.563920000e+05 <= 6.569231603e+05 < 0.1% (87; 35) + 241: mip = 6.563920000e+05 <= tree is empty 0.0% (0; 213) INTEGER OPTIMAL SOLUTION FOUND Time used: 0.0 secs Memory used: 0.2 Mb (231419 bytes) Writing MIP solution to 'C:\Users\CASSIA~1\AppData\Local\Temp\tmpb6ikms_5.glpk.raw'... 256 lines were written Solution is feasible and optimal Objective function value = 656392.0
#Impressão da variável Y - Variável de decisão: 1 se o projeto i é selecionado para investimento
for i in model.set_I:
if pyo.value(Y[i]) > 1.E-3:
print(f'Y[{i}] = {round(pyo.value(Y[i]),0)}')
Y[0] = 1.0 Y[3] = 1.0 Y[5] = 1.0 Y[6] = 1.0 Y[9] = 1.0 Y[11] = 1.0 Y[12] = 1.0 Y[13] = 1.0 Y[14] = 1.0 Y[15] = 1.0 Y[16] = 1.0 Y[19] = 1.0 Y[20] = 1.0 Y[22] = 1.0 Y[26] = 1.0 Y[27] = 1.0 Y[29] = 1.0 Y[30] = 1.0 Y[31] = 1.0 Y[34] = 1.0 Y[35] = 1.0 Y[38] = 1.0 Y[39] = 1.0
# Lista construída iterativamente, que se tornará o DataFrame final de solução
df_sol_Y = []
for i in model.set_I:
if pyo.value(Y[i]) > 1.E-3:
# Valor da variável
#valor = round(X[i][j][t],2)
# Linha do DataFrame
dict_linha = {
'Projeto': i ,
}
# Guardando valor
df_sol_Y.append(dict_linha)
# Criando DataFrame
df_sol_Y = pd.DataFrame(df_sol_Y)
df_sol_Y = df_sol_Y.sort_values(['Projeto'])
df_sol_Y.index = list(range(len(df_sol_Y)))
print(df_sol_Y)
Projeto 0 0 1 3 2 5 3 6 4 9 5 11 6 12 7 13 8 14 9 15 10 16 11 19 12 20 13 22 14 26 15 27 16 29 17 30 18 31 19 34 20 35 21 38 22 39
#Criação do vetor com os nomes dos projetos
# Inicializa a matriz com zeros
nome_i = [0 for e in dict_I.keys()]
# Preenche as posições de acordo com df_carteira_investimentos
for linha in df_carteira_investimentos.iterrows():
# Extrai o conteúdo da linha atual
dados_linha = list(linha[1])
#print(dados_linha)
# Define o índice do projeto de investimento
try:
# Procura o índice na lista do projeto de investimento
i = list(dict_I.keys())[list(dict_I.values()).index(dados_linha[0])]
nome_i[i]= dados_linha[1]
except:
# Projeto de investimento não encontrado
i = -1
print(nome_i)
['MASTER 5500 D - PADRÃO - BRA', 'MASTER 5500 D C/ ESTEIRA PRECISA - PADRÃO - BRA', 'MASTER 5500 D C/ ESTEIRA PRECISA C/ PNEU 11L15 - PADRÃO - BRA', 'MASTER 5500 D C/ DIRECIONADOR - PADRÃO - BRA', 'MASTER 7500 D - PADRÃO - BRA', 'MASTER 7500 D C/ ESTEIRA PRECISA - PADRÃO - BRA', 'MASTER 7500 D C/ PNEU 11L15 - PADRÃO - BRA', 'MASTER 7500 D C/ ESTEIRA PRECISA C/ PNEU 11L15 - PADRÃO - BRA', 'MASTER 2500 D - PADRÃO - BRA', 'MASTER 2500 D C/ ESTEIRA PRECISA - PADRÃO - BRA', 'MASTER 2500 D CAFEEIRA C/ ESTEIRA PRECISA - PADRÃO - BRA', 'MASTER 2500 D CAFEEIRA C/ ESTEIRA PRECISA C/ DIRECIONADOR - PADRÃO - BRA', 'MASTER 5500 D - VERMELHO - BRA', 'MASTER 5500 D - VERDE - BRA', 'MASTER 5500 D - AMARELO - BRA', 'MASTER 5500 D - AZUL - BRA', 'MASTER 5500 D C/ ESTEIRA PRECISA - VERMELHO - BRA', 'MASTER 5500 D C/ ESTEIRA PRECISA - VERDE - BRA', 'MASTER 5500 D C/ ESTEIRA PRECISA - AMARELO - BRA', 'MASTER 5500 D C/ ESTEIRA PRECISA - AZUL - BRA', 'MASTER 5500 D C/ ESTEIRA PRECISA C/ PNEU 11L15 - VERMELHO - BRA', 'MASTER 5500 D C/ ESTEIRA PRECISA C/ PNEU 11L15 - VERDE - BRA', 'MASTER 5500 D C/ ESTEIRA PRECISA C/ PNEU 11L15 - AMARELO - BRA', 'MASTER 5500 D C/ ESTEIRA PRECISA C/ PNEU 11L15 - AZUL - BRA', 'MASTER 5500 D C/ DIRECIONADOR - VERMELHO - BRA', 'MASTER 5500 D C/ DIRECIONADOR - VERDE - BRA', 'MASTER 5500 D C/ DIRECIONADOR - AMARELO - BRA', 'MASTER 5500 D C/ DIRECIONADOR - AZUL - BRA', 'MASTER 7500 D - VERMELHO - BRA', 'MASTER 7500 D - VERDE - BRA', 'MASTER 7500 D - AMARELO - BRA', 'MASTER 7500 D - AZUL - BRA', 'MASTER 7500 D C/ ESTEIRA PRECISA - VERMELHO - BRA', 'MASTER 7500 D C/ ESTEIRA PRECISA - VERDE - BRA', 'MASTER 7500 D C/ ESTEIRA PRECISA - AMARELO - BRA', 'MASTER 7500 D C/ ESTEIRA PRECISA - AZUL - BRA', 'MASTER 7500 D C/ PNEU 11L15 - VERMELHO - BRA', 'MASTER 7500 D C/ PNEU 11L15 - VERDE - BRA', 'MASTER 7500 D C/ PNEU 11L15 - AMARELO - BRA', 'MASTER 7500 D C/ PNEU 11L15 - AZUL - BRA']
#Manipulando DataFrame sol_Y para a construção de gráficos
#Adicionando as colunas de custo do projeto, vpl do projeto e descrição do projeto
# Coluna com os custos de cada projeto
custo_projeto = []
# Coluna com o vpl de cada projeto
vpl_projeto = []
# Coluna com o nome de cada projeto
nome_projeto = []
# Preenche as posições de acordo com df_sol_X
for linha in df_sol_Y.iterrows():
# Extrai o conteúdo da linha atual
dados_linha = list(linha[1])
#print(dados_linha)
custo_projeto.append(c_i[dados_linha[0]])
vpl_projeto.append(vpl_i[dados_linha[0]])
nome_projeto.append(nome_i[dados_linha[0]])
# Adicionanado a coluna com os custos de cada projeto no Dataframe da solução
df_sol_Y["Custo realizado"] = [elemento for elemento in custo_projeto]
# Adicionanado a coluna com o VPL de cada projeto no Dataframe da solução
df_sol_Y["VPL realizado"] = [elemento for elemento in vpl_projeto]
# Adicionanado a coluna com os nomes de cada projeto no Dataframe da solução
df_sol_Y["Nome do Projeto"] = [elemento for elemento in nome_projeto]
# Cálculo do total investido
total_investido = np.sum(custo_projeto)
print(total_investido)
# Calculo do VPL total esperado
vpl_total = np.sum(vpl_projeto)
print(vpl_total)
299316 656392
#Criando gráfico dos custos totais de investimento
fig_custo_projeto = px.bar(df_sol_Y, x="Nome do Projeto", y="Custo realizado", text = "Custo realizado", height=1100, width = 1300, color = "Nome do Projeto",title="Custos dos investimentos realizados nos projetos")
# Total investido
#fig_custo_projeto.add_trace(go.Scatter(x=df_sol_Y["Nome do Projeto"], y=[total_investido for nome in df_sol_Y["Nome do Projeto"] ],
#line=dict(color='royalblue', width=2, dash='dot'),
#name='Custo total investido'))
# Budget
#fig_custo_projeto.add_trace(go.Scatter(x=df_sol_Y["Nome do Projeto"], y=[M for nome in df_sol_Y["Nome do Projeto"] ],
#line = dict(color='firebrick', width=2, dash='dot'),
#name='Budget para investimento'))
fig_custo_projeto.show()
#Criando gráfico dos VPLS totais de investimento
fig_vpl_total = px.pie(df_sol_Y, values='VPL realizado',
names='Nome do Projeto', title="VPL dos projetos")
fig_vpl_total.show()
fig_analise_vpl = px.bar(df_sol_Y, x="Nome do Projeto", y="VPL realizado", text = "VPL realizado", height=1100, width = 1300, color = "Nome do Projeto",title="VPL dos projetos")
# Total investido
#fig_analise_vpl.add_trace(go.Scatter(x=df_sol_Y["Nome do Projeto"], y=[total_investido for nome in df_sol_Y["Nome do Projeto"] ],
#line=dict(color='royalblue', width=2, dash='dot'),
#name='Custo total investido'))
# VPL retornado
#fig_analise_vpl.add_trace(go.Scatter(x=df_sol_Y["Nome do Projeto"], y=[vpl_total for nome in df_sol_Y["Nome do Projeto"] ],
#line = dict(color='firebrick', width=2, dash='dot'),
#name='VPL retornado'))
fig_analise_vpl.show()
dict_final={'Valores': [total_investido,vpl_total], 'Descrição': ['Custo Total', 'VPL Total']}
df_analise_final = pd.DataFrame(data=dict_final)
#rint(df_analise_final)
fig_vpl = px.bar(df_analise_final, color = 'Descrição')
fig_vpl.show()